<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ECharts极坐标转换实现角度线绘制</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            color: #fff;
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        header {
            text-align: center;
            padding: 20px 0;
            margin-bottom: 30px;
        }
        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
        }
        .subtitle {
            font-size: 1.2rem;
            opacity: 0.9;
            max-width: 800px;
            margin: 0 auto;
            line-height: 1.6;
        }
        .content {
            display: flex;
            flex-wrap: wrap;
            gap: 30px;
            margin-bottom: 30px;
        }
        .chart-container {
            flex: 1;
            min-width: 300px;
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 15px;
            padding: 20px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        .chart-title {
            font-size: 1.5rem;
            margin-bottom: 15px;
            text-align: center;
            color: #ffcc00;
        }
        .chart {
            height: 400px;
            width: 100%;
        }
        .controls {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            justify-content: center;
            margin-top: 20px;
        }
        .control-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        label {
            font-weight: 600;
            font-size: 0.9rem;
        }
        input[type="range"] {
            width: 200px;
        }
        button {
            background: linear-gradient(to right, #ff8a00, #da1b60);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 30px;
            cursor: pointer;
            font-weight: 600;
            transition: all 0.3s ease;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
        }
        button:hover {
            transform: translateY(-3px);
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
        }
        .explanation {
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 15px;
            padding: 25px;
            margin-top: 30px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        .explanation h2 {
            color: #ffcc00;
            margin-bottom: 15px;
            font-size: 1.8rem;
        }
        .explanation p {
            line-height: 1.7;
            margin-bottom: 15px;
        }
        .formula {
            background: rgba(0, 0, 0, 0.3);
            padding: 15px;
            border-radius: 10px;
            margin: 15px 0;
            font-family: 'Courier New', monospace;
            font-size: 1.1rem;
        }
        .highlight {
            color: #ffcc00;
            font-weight: bold;
        }
        footer {
            text-align: center;
            margin-top: 40px;
            padding: 20px;
            font-size: 0.9rem;
            opacity: 0.8;
        }
        @media (max-width: 768px) {
            .content {
                flex-direction: column;
            }
            .chart-container {
                min-width: 100%;
            }
        }
    </style>
</head>
<body>
<div class="container">
    <header>
        <h1>ECharts极坐标转换与角度线绘制</h1>
        <p class="subtitle">探索极坐标系下角度线的绘制方法，实现笛卡尔坐标与极坐标之间的转换，并可视化转换过程。</p>
    </header>

    <div class="content">
        <div class="chart-container">
            <h2 class="chart-title">极坐标系角度线</h2>
            <div id="polarChart" class="chart"></div>
        </div>

        <div class="chart-container">
            <h2 class="chart-title">笛卡尔坐标系</h2>
            <div id="cartesianChart" class="chart"></div>
        </div>
    </div>

    <div class="controls">
        <div class="control-group">
            <label for="angle">角度 (度): <span id="angleValue">45</span>°</label>
            <input type="range" id="angle" min="0" max="360" value="45">
        </div>
        <div class="control-group">
            <label for="radius">半径: <span id="radiusValue">5</span></label>
            <input type="range" id="radius" min="1" max="10" value="5">
        </div>
        <button id="animateBtn">动画演示转换</button>
        <button id="resetBtn">重置</button>
    </div>

    <div class="explanation">
        <h2>极坐标转换原理</h2>
        <p>极坐标系使用角度和距离来表示点的位置，与笛卡尔坐标系(x, y)的转换关系如下：</p>
        <div class="formula">
            x = r × cos(θ)<br>
            y = r × sin(θ)<br>
            其中 r 是极径，θ 是极角（弧度）
        </div>
        <p>在极坐标系中绘制角度线，实际上是在固定角度的情况下，改变半径值得到的一系列点。当我们在极坐标系中绘制一条角度线时，实际上是在绘制一条从原点出发的射线。</p>
        <p>使用<span class="highlight">ECharts</span>的极坐标系功能，我们可以轻松实现角度线的绘制，并通过坐标转换在笛卡尔坐标系中展示相同的点。</p>
        <p>调整上方的控制滑块可以改变角度和半径值，观察两个坐标系中的变化。点击"动画演示转换"按钮可以查看坐标转换的动画过程。</p>
    </div>

    <footer>
        <p>ECharts 极坐标转换可视化示例 | 使用 ECharts 5.4.3 实现</p>
    </footer>
</div>

<script>
    // 初始化ECharts实例
    const polarChart = echarts.init(document.getElementById('polarChart'));
    const cartesianChart = echarts.init(document.getElementById('cartesianChart'));

    // 初始角度和半径
    let currentAngle = 45;
    let currentRadius = 5;

    // 角度转弧度函数
    function degToRad(deg) {
        return deg * Math.PI / 180;
    }

    // 极坐标转笛卡尔坐标
    function polarToCartesian(angle, radius) {
        const rad = degToRad(angle);
        return {
            x: radius * Math.cos(rad),
            y: radius * Math.sin(rad)
        };
    }

    // 生成角度线数据（极坐标）
    function generatePolarLineData(angle, radius) {
        const data = [];
        for (let r = 0; r <= radius; r += 0.1) {
            data.push([angle, r]);
        }
        return data;
    }

    // 生成角度线数据（笛卡尔坐标）
    function generateCartesianLineData(angle, radius) {
        const data = [];
        for (let r = 0; r <= radius; r += 0.1) {
            const point = polarToCartesian(angle, r);
            data.push([point.x, point.y]);
        }
        return data;
    }

    // 生成极坐标网格线数据
    function generatePolarGridLines() {
        const lines = [];
        // 角度线
        for (let angle = 0; angle < 360; angle += 30) {
            const data = [];
            for (let r = 0; r <= 10; r += 0.5) {
                data.push([angle, r]);
            }
            lines.push({
                name: `${angle}°`,
                type: 'line',
                coordinateSystem: 'polar',
                data: data,
                lineStyle: {
                    color: 'rgba(255, 255, 255, 0.2)',
                    width: 1
                },
                silent: true
            });
        }
        // 半径圆
        for (let r = 2; r <= 10; r += 2) {
            const data = [];
            for (let angle = 0; angle <= 360; angle += 1) {
                data.push([angle, r]);
            }
            lines.push({
                name: `r=${r}`,
                type: 'line',
                coordinateSystem: 'polar',
                data: data,
                lineStyle: {
                    color: 'rgba(255, 255, 255, 0.2)',
                    width: 1
                },
                silent: true
            });
        }
        return lines;
    }

    // 生成笛卡尔坐标网格线数据
    function generateCartesianGridLines() {
        const lines = [];
        // x轴网格线
        for (let x = -10; x <= 10; x += 2) {
            lines.push({
                name: `x=${x}`,
                type: 'line',
                data: [[x, -10], [x, 10]],
                lineStyle: {
                    color: 'rgba(255, 255, 255, 0.2)',
                    width: 1
                },
                silent: true
            });
        }
        // y轴网格线
        for (let y = -10; y <= 10; y += 2) {
            lines.push({
                name: `y=${y}`,
                type: 'line',
                data: [[-10, y], [10, y]],
                lineStyle: {
                    color: 'rgba(255, 255, 255, 0.2)',
                    width: 1
                },
                silent: true
            });
        }
        return lines;
    }

    // 更新图表
    function updateCharts(angle, radius) {
        // 极坐标配置
        const polarOption = {
            backgroundColor: 'transparent',
            angleAxis: {
                type: 'value',
                startAngle: 0,
                min: 0,
                max: 360,
                splitLine: {
                    show: false
                },
                axisLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLabel: {
                    color: '#fff'
                }
            },
            radiusAxis: {
                min: 0,
                max: 10,
                splitLine: {
                    show: false
                },
                axisLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLabel: {
                    color: '#fff'
                }
            },
            polar: {
                center: ['50%', '50%'],
                radius: '80%'
            },
            series: [
                ...generatePolarGridLines(),
                {
                    name: '角度线',
                    type: 'line',
                    coordinateSystem: 'polar',
                    data: generatePolarLineData(angle, radius),
                    lineStyle: {
                        color: '#ffcc00',
                        width: 3
                    },
                    symbol: 'none',
                    emphasis: {
                        lineStyle: {
                            width: 5
                        }
                    }
                },
                {
                    name: '当前点',
                    type: 'scatter',
                    coordinateSystem: 'polar',
                    data: [[angle, radius]],
                    symbolSize: 15,
                    itemStyle: {
                        color: '#ff3333'
                    }
                }
            ],
            animation: true,
            animationDuration: 500
        };

        // 笛卡尔坐标配置
        const cartesianOption = {
            backgroundColor: 'transparent',
            xAxis: {
                type: 'value',
                min: -10,
                max: 10,
                splitLine: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        color: '#fff'
                    }
                },
                axisLabel: {
                    color: '#fff'
                }
            },
            yAxis: {
                type: 'value',
                min: -10,
                max: 10,
                splitLine: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        color: '#fff'
                    }
                },
                axisLabel: {
                    color: '#fff'
                }
            },
            series: [
                ...generateCartesianGridLines(),
                {
                    name: '角度线',
                    type: 'line',
                    data: generateCartesianLineData(angle, radius),
                    lineStyle: {
                        color: '#ffcc00',
                        width: 3
                    },
                    symbol: 'none',
                    emphasis: {
                        lineStyle: {
                            width: 5
                        }
                    }
                },
                {
                    name: '当前点',
                    type: 'scatter',
                    data: [[polarToCartesian(angle, radius).x, polarToCartesian(angle, radius).y]],
                    symbolSize: 15,
                    itemStyle: {
                        color: '#ff3333'
                    }
                }
            ],
            animation: true,
            animationDuration: 500
        };

        polarChart.setOption(polarOption, true);
        cartesianChart.setOption(cartesianOption, true);
    }

    // 初始化图表
    updateCharts(currentAngle, currentRadius);

    // 事件监听
    document.getElementById('angle').addEventListener('input', function() {
        currentAngle = parseInt(this.value);
        document.getElementById('angleValue').textContent = currentAngle;
        updateCharts(currentAngle, currentRadius);
    });

    document.getElementById('radius').addEventListener('input', function() {
        currentRadius = parseInt(this.value);
        document.getElementById('radiusValue').textContent = currentRadius;
        updateCharts(currentAngle, currentRadius);
    });

    document.getElementById('animateBtn').addEventListener('click', function() {
        // 简单动画：从0到当前角度和半径
        let angle = 0;
        let radius = 0;
        const interval = setInterval(() => {
            angle += 5;
            radius += 0.5;

            if (angle >= currentAngle && radius >= currentRadius) {
                angle = currentAngle;
                radius = currentRadius;
                clearInterval(interval);
            }

            updateCharts(angle, radius);
        }, 50);
    });

    document.getElementById('resetBtn').addEventListener('click', function() {
        currentAngle = 45;
        currentRadius = 5;
        document.getElementById('angle').value = currentAngle;
        document.getElementById('radius').value = currentRadius;
        document.getElementById('angleValue').textContent = currentAngle;
        document.getElementById('radiusValue').textContent = currentRadius;
        updateCharts(currentAngle, currentRadius);
    });

    // 响应窗口大小变化
    window.addEventListener('resize', function() {
        polarChart.resize();
        cartesianChart.resize();
    });
</script>
</body>
</html>